<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>

<meta charset="utf-8">
<meta name="generator" content="quarto-1.8.23">

<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">

<meta name="author" content="Duc-Tam Nguyen">
<meta name="dcterms.date" content="2025-09-26">

<title>The Little Book of Linear Algebra</title>
<style>
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
div.columns{display: flex; gap: min(4vw, 1.5em);}
div.column{flex: auto; overflow-x: auto;}
div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
ul.task-list{list-style: none;}
ul.task-list li input[type="checkbox"] {
  width: 0.8em;
  margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */ 
  vertical-align: middle;
}
</style>


<script src="site_libs/quarto-nav/quarto-nav.js"></script>
<script src="site_libs/quarto-nav/headroom.min.js"></script>
<script src="site_libs/clipboard/clipboard.min.js"></script>
<script src="site_libs/quarto-search/autocomplete.umd.js"></script>
<script src="site_libs/quarto-search/fuse.min.js"></script>
<script src="site_libs/quarto-search/quarto-search.js"></script>
<meta name="quarto:offset" content="./">
<link href="./books/en-US/book.html" rel="next">
<script src="site_libs/quarto-html/quarto.js" type="module"></script>
<script src="site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
<script src="site_libs/quarto-html/axe/axe-check.js" type="module"></script>
<script src="site_libs/quarto-html/popper.min.js"></script>
<script src="site_libs/quarto-html/tippy.umd.min.js"></script>
<script src="site_libs/quarto-html/anchor.min.js"></script>
<link href="site_libs/quarto-html/tippy.css" rel="stylesheet">
<link href="site_libs/quarto-html/quarto-syntax-highlighting-1fe81d0376b2c50856e68e651e390326.css" rel="stylesheet" id="quarto-text-highlighting-styles">
<script src="site_libs/bootstrap/bootstrap.min.js"></script>
<link href="site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
<link href="site_libs/bootstrap/bootstrap-27c261d06b905028a18691de25d09dde.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
<script id="quarto-search-options" type="application/json">{
  "location": "sidebar",
  "copy-button": false,
  "collapse-after": 3,
  "panel-placement": "start",
  "type": "textbox",
  "limit": 50,
  "keyboard-shortcut": [
    "f",
    "/",
    "s"
  ],
  "show-item-context": false,
  "language": {
    "search-no-results-text": "No results",
    "search-matching-documents-text": "matching documents",
    "search-copy-link-title": "Copy link to search",
    "search-hide-matches-text": "Hide additional matches",
    "search-more-match-text": "more match in this document",
    "search-more-matches-text": "more matches in this document",
    "search-clear-button-title": "Clear",
    "search-text-placeholder": "",
    "search-detached-cancel-button-title": "Cancel",
    "search-submit-button-title": "Submit",
    "search-label": "Search"
  }
}</script>


</head>

<body class="nav-sidebar floating quarto-light">

<div id="quarto-search-results"></div>
  <header id="quarto-header" class="headroom fixed-top">
  <nav class="quarto-secondary-nav">
    <div class="container-fluid d-flex">
      <button type="button" class="quarto-btn-toggle btn" data-bs-toggle="collapse" role="button" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
        <i class="bi bi-layout-text-sidebar-reverse"></i>
      </button>
        <nav class="quarto-page-breadcrumbs" aria-label="breadcrumb"><ol class="breadcrumb"><li class="breadcrumb-item"><a href="./index.html"><span class="chapter-title">Content</span></a></li></ol></nav>
        <a class="flex-grow-1" role="navigation" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item" aria-controls="quarto-sidebar" aria-expanded="false" aria-label="Toggle sidebar navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">      
        </a>
      <button type="button" class="btn quarto-search-button" aria-label="Search" onclick="window.quartoOpenSearch();">
        <i class="bi bi-search"></i>
      </button>
    </div>
  </nav>
</header>
<!-- content -->
<div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article">
<!-- sidebar -->
  <nav id="quarto-sidebar" class="sidebar collapse collapse-horizontal quarto-sidebar-collapse-item sidebar-navigation floating overflow-auto">
    <div class="pt-lg-2 mt-2 text-left sidebar-header sidebar-header-stacked">
      <a href="./index.html" class="sidebar-logo-link">
      </a>
    <div class="sidebar-title mb-0 py-0">
      <a href="./">The Little Book of Linear Algebra</a> 
    </div>
      </div>
        <div class="mt-2 flex-shrink-0 align-items-center">
        <div class="sidebar-search">
        <div id="quarto-search" class="" title="Search"></div>
        </div>
        </div>
    <div class="sidebar-menu-container"> 
    <ul class="list-unstyled mt-1">
        <li class="sidebar-item">
  <div class="sidebar-item-container"> 
  <a href="./index.html" class="sidebar-item-text sidebar-link active"><span class="chapter-title">Content</span></a>
  </div>
</li>
        <li class="sidebar-item">
  <div class="sidebar-item-container"> 
  <a href="./books/en-US/book.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">The Book</span></a>
  </div>
</li>
        <li class="sidebar-item">
  <div class="sidebar-item-container"> 
  <a href="./books/en-US/lab.html" class="sidebar-item-text sidebar-link"><span class="chapter-title">The LAB</span></a>
  </div>
</li>
    </ul>
    </div>
    <nav id="TOC" role="doc-toc" class="toc-active">
    <h2 id="toc-title">Table of contents</h2>
   
  <ul>
  <li><a href="#content" id="toc-content" class="nav-link active" data-scroll-target="#content">Content</a></li>
  </ul>
</nav>
</nav>
<div id="quarto-sidebar-glass" class="quarto-sidebar-collapse-item" data-bs-toggle="collapse" data-bs-target=".quarto-sidebar-collapse-item"></div>
<!-- margin-sidebar -->
    <div id="quarto-margin-sidebar" class="sidebar margin-sidebar zindex-bottom">
    </div>
<!-- main -->
<main class="content" id="quarto-document-content">

<header id="title-block-header" class="quarto-title-block default">
<div class="quarto-title">
<h1 class="title">The Little Book of Linear Algebra</h1>
<p class="subtitle lead">Version 0.3.0</p>
</div>



<div class="quarto-title-meta">

    <div>
    <div class="quarto-title-meta-heading">Author</div>
    <div class="quarto-title-meta-contents">
             <p>Duc-Tam Nguyen </p>
          </div>
  </div>
    
    <div>
    <div class="quarto-title-meta-heading">Published</div>
    <div class="quarto-title-meta-contents">
      <p class="date">September 26, 2025</p>
    </div>
  </div>
  
    
  </div>
  


</header>


<section id="content" class="level1">
<h1>Content</h1>
<section id="chapter-1.-vectors-scalars-and-geometry" class="level4">
<h4 class="anchored" data-anchor-id="chapter-1.-vectors-scalars-and-geometry">Chapter 1. Vectors, Scalars, and Geometry</h4>
<ol type="1">
<li>Scalars, vectors, and coordinate systems (what they are, why we care)</li>
<li>Vector notation, components, and arrows (reading and writing vectors)</li>
<li>Vector addition and scalar multiplication (the two basic moves)</li>
<li>Linear combinations and span (building new vectors from old ones)</li>
<li>Length (norm) and distance (how big and how far)</li>
<li>Dot product (algebraic and geometric views)</li>
<li>Angles between vectors and cosine (measuring alignment)</li>
<li>Projections and decompositions (splitting along a direction)</li>
<li>Cauchy–Schwarz and triangle inequalities (two fundamental bounds)</li>
<li>Orthonormal sets in ℝ²/ℝ³ (nice bases you already know)</li>
</ol>
</section>
<section id="chapter-2.-matrices-and-basic-operations" class="level4">
<h4 class="anchored" data-anchor-id="chapter-2.-matrices-and-basic-operations">Chapter 2. Matrices and Basic Operations</h4>
<ol start="11" type="1">
<li>Matrices as tables and as machines (two mental models)</li>
<li>Matrix shapes, indexing, and block views (seeing structure)</li>
<li>Matrix addition and scalar multiplication (componentwise rules)</li>
<li>Matrix–vector product (linear combos of columns)</li>
<li>Matrix–matrix product (composition of linear steps)</li>
<li>Identity, inverse, and transpose (three special friends)</li>
<li>Symmetric, diagonal, triangular, and permutation matrices (special families)</li>
<li>Trace and basic matrix properties (quick invariants)</li>
<li>Affine transforms and homogeneous coordinates (translations included)</li>
<li>Computing with matrices (cost counts and simple speedups)</li>
</ol>
</section>
<section id="chapter-3.-linear-systems-and-elimination" class="level4">
<h4 class="anchored" data-anchor-id="chapter-3.-linear-systems-and-elimination">Chapter 3. Linear Systems and Elimination</h4>
<ol start="21" type="1">
<li>From equations to matrices (augmenting and encoding)</li>
<li>Row operations (legal moves that keep solutions)</li>
<li>Row-echelon and reduced row-echelon forms (target shapes)</li>
<li>Pivots, free variables, and leading ones (reading solutions)</li>
<li>Solving consistent systems (unique vs.&nbsp;infinite solutions)</li>
<li>Detecting inconsistency (when no solution exists)</li>
<li>Gaussian elimination by hand (a disciplined procedure)</li>
<li>Back substitution and solution sets (finishing cleanly)</li>
<li>Rank and its first meaning (pivots as information)</li>
<li>LU factorization (elimination captured as L and U)</li>
</ol>
</section>
<section id="chapter-4.-vector-spaces-and-subspaces" class="level4">
<h4 class="anchored" data-anchor-id="chapter-4.-vector-spaces-and-subspaces">Chapter 4. Vector Spaces and Subspaces</h4>
<ol start="31" type="1">
<li>Axioms of vector spaces (what “space” really means)</li>
<li>Subspaces, column space, and null space (where solutions live)</li>
<li>Span and generating sets (coverage of a space)</li>
<li>Linear independence and dependence (no redundancy vs.&nbsp;redundancy)</li>
<li>Basis and coordinates (naming every vector uniquely)</li>
<li>Dimension (how many directions)</li>
<li>Rank–nullity theorem (dimensions that add up)</li>
<li>Coordinates relative to a basis (changing the “ruler”)</li>
<li>Change-of-basis matrices (moving between coordinate systems)</li>
<li>Affine subspaces (lines and planes not through the origin)</li>
</ol>
</section>
<section id="chapter-5.-linear-transformations-and-structure" class="level4">
<h4 class="anchored" data-anchor-id="chapter-5.-linear-transformations-and-structure">Chapter 5. Linear Transformations and Structure</h4>
<ol start="41" type="1">
<li>Linear transformations (preserving lines and sums)</li>
<li>Matrix representation of a linear map (choosing a basis)</li>
<li>Kernel and image (inputs that vanish; outputs we can reach)</li>
<li>Invertibility and isomorphisms (perfectly reversible maps)</li>
<li>Composition, powers, and iteration (doing it again and again)</li>
<li>Similarity and conjugation (same action, different basis)</li>
<li>Projections and reflections (idempotent and involutive maps)</li>
<li>Rotations and shear (geometric intuition)</li>
<li>Rank and operator viewpoint (rank beyond elimination)</li>
<li>Block matrices and block maps (divide and conquer structure)</li>
</ol>
</section>
<section id="chapter-6.-determinants-and-volume" class="level4">
<h4 class="anchored" data-anchor-id="chapter-6.-determinants-and-volume">Chapter 6. Determinants and Volume</h4>
<ol start="51" type="1">
<li>Areas, volumes, and signed scale factors (geometric entry point)</li>
<li>Determinant via linear rules (multilinearity, sign, normalization)</li>
<li>Determinant and row operations (how each move changes det)</li>
<li>Triangular matrices and product of diagonals (fast wins)</li>
<li>det(AB) = det(A)det(B) (multiplicative magic)</li>
<li>Invertibility and zero determinant (flat vs.&nbsp;full volume)</li>
<li>Cofactor expansion (Laplace’s method)</li>
<li>Permutations and sign (the combinatorial core)</li>
<li>Cramer’s rule (solving with determinants, and when not to use it)</li>
<li>Computing determinants in practice (use LU, mind stability)</li>
</ol>
</section>
<section id="chapter-7.-eigenvalues-eigenvectors-and-dynamics" class="level4">
<h4 class="anchored" data-anchor-id="chapter-7.-eigenvalues-eigenvectors-and-dynamics">Chapter 7. Eigenvalues, Eigenvectors, and Dynamics</h4>
<ol start="61" type="1">
<li>Eigenvalues and eigenvectors (directions that stay put)</li>
<li>Characteristic polynomial (where eigenvalues come from)</li>
<li>Algebraic vs.&nbsp;geometric multiplicity (how many and how independent)</li>
<li>Diagonalization (when a matrix becomes simple)</li>
<li>Powers of a matrix (long-term behavior via eigenvalues)</li>
<li>Real vs.&nbsp;complex spectra (rotations and oscillations)</li>
<li>Defective matrices and a peek at Jordan form (when diagonalization fails)</li>
<li>Stability and spectral radius (grow, decay, or oscillate)</li>
<li>Markov chains and steady states (probabilities as linear algebra)</li>
<li>Linear differential systems (solutions via eigen-decomposition)</li>
</ol>
</section>
<section id="chapter-8.-orthogonality-least-squares-and-qr" class="level4">
<h4 class="anchored" data-anchor-id="chapter-8.-orthogonality-least-squares-and-qr">Chapter 8. Orthogonality, Least Squares, and QR</h4>
<ol start="71" type="1">
<li>Inner products beyond dot product (custom notions of angle)</li>
<li>Orthogonality and orthonormal bases (perpendicular power)</li>
<li>Gram–Schmidt process (constructing orthonormal bases)</li>
<li>Orthogonal projections onto subspaces (closest point principle)</li>
<li>Least-squares problems (fit when exact solve is impossible)</li>
<li>Normal equations and geometry of residuals (why it works)</li>
<li>QR factorization (stable least squares via orthogonality)</li>
<li>Orthogonal matrices (length-preserving transforms)</li>
<li>Fourier viewpoint (expanding in orthogonal waves)</li>
<li>Polynomial and multifeature least squares (fitting more flexibly)</li>
</ol>
</section>
<section id="chapter-9.-svd-pca-and-conditioning" class="level4">
<h4 class="anchored" data-anchor-id="chapter-9.-svd-pca-and-conditioning">Chapter 9. SVD, PCA, and Conditioning</h4>
<ol start="81" type="1">
<li>Singular values and SVD (universal factorization)</li>
<li>Geometry of SVD (rotations + stretching)</li>
<li>Relation to eigen-decompositions (ATA and AAT)</li>
<li>Low-rank approximation (best small models)</li>
<li>Principal component analysis (variance and directions)</li>
<li>Pseudoinverse (Moore–Penrose) and solving ill-posed systems</li>
<li>Conditioning and sensitivity (how errors amplify)</li>
<li>Matrix norms and singular values (measuring size properly)</li>
<li>Regularization (ridge/Tikhonov to tame instability)</li>
<li>Rank-revealing QR and practical diagnostics (what rank really is)</li>
</ol>
</section>
<section id="chapter-10.-applications-and-computation" class="level4">
<h4 class="anchored" data-anchor-id="chapter-10.-applications-and-computation">Chapter 10. Applications and Computation</h4>
<ol start="91" type="1">
<li>2D/3D geometry pipelines (cameras, rotations, and transforms)</li>
<li>Computer graphics and robotics (homogeneous tricks in action)</li>
<li>Graphs, adjacency, and Laplacians (networks via matrices)</li>
<li>Data preprocessing as linear ops (centering, whitening, scaling)</li>
<li>Linear regression and classification (from model to matrix)</li>
<li>PCA in practice (dimensionality reduction workflow)</li>
<li>Recommender systems and low-rank models (fill the missing entries)</li>
<li>PageRank and random walks (ranking with eigenvectors)</li>
<li>Numerical linear algebra essentials (floating point, BLAS/LAPACK)</li>
<li>Capstone problem sets and next steps (a roadmap to mastery)</li>
</ol>


</section>
</section>

</main> <!-- /main -->
<script id="quarto-html-after-body" type="application/javascript">
  window.document.addEventListener("DOMContentLoaded", function (event) {
    const icon = "";
    const anchorJS = new window.AnchorJS();
    anchorJS.options = {
      placement: 'right',
      icon: icon
    };
    anchorJS.add('.anchored');
    const isCodeAnnotation = (el) => {
      for (const clz of el.classList) {
        if (clz.startsWith('code-annotation-')) {                     
          return true;
        }
      }
      return false;
    }
    const onCopySuccess = function(e) {
      // button target
      const button = e.trigger;
      // don't keep focus
      button.blur();
      // flash "checked"
      button.classList.add('code-copy-button-checked');
      var currentTitle = button.getAttribute("title");
      button.setAttribute("title", "Copied!");
      let tooltip;
      if (window.bootstrap) {
        button.setAttribute("data-bs-toggle", "tooltip");
        button.setAttribute("data-bs-placement", "left");
        button.setAttribute("data-bs-title", "Copied!");
        tooltip = new bootstrap.Tooltip(button, 
          { trigger: "manual", 
            customClass: "code-copy-button-tooltip",
            offset: [0, -8]});
        tooltip.show();    
      }
      setTimeout(function() {
        if (tooltip) {
          tooltip.hide();
          button.removeAttribute("data-bs-title");
          button.removeAttribute("data-bs-toggle");
          button.removeAttribute("data-bs-placement");
        }
        button.setAttribute("title", currentTitle);
        button.classList.remove('code-copy-button-checked');
      }, 1000);
      // clear code selection
      e.clearSelection();
    }
    const getTextToCopy = function(trigger) {
      const outerScaffold = trigger.parentElement.cloneNode(true);
      const codeEl = outerScaffold.querySelector('code');
      for (const childEl of codeEl.children) {
        if (isCodeAnnotation(childEl)) {
          childEl.remove();
        }
      }
      return codeEl.innerText;
    }
    const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
      text: getTextToCopy
    });
    clipboard.on('success', onCopySuccess);
    if (window.document.getElementById('quarto-embedded-source-code-modal')) {
      const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
        text: getTextToCopy,
        container: window.document.getElementById('quarto-embedded-source-code-modal')
      });
      clipboardModal.on('success', onCopySuccess);
    }
      var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
      var mailtoRegex = new RegExp(/^mailto:/);
        var filterRegex = new RegExp('/' + window.location.host + '/');
      var isInternal = (href) => {
          return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
      }
      // Inspect non-navigation links and adorn them if external
     var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
      for (var i=0; i<links.length; i++) {
        const link = links[i];
        if (!isInternal(link.href)) {
          // undo the damage that might have been done by quarto-nav.js in the case of
          // links that we want to consider external
          if (link.dataset.originalHref !== undefined) {
            link.href = link.dataset.originalHref;
          }
        }
      }
    function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
      const config = {
        allowHTML: true,
        maxWidth: 500,
        delay: 100,
        arrow: false,
        appendTo: function(el) {
            return el.parentElement;
        },
        interactive: true,
        interactiveBorder: 10,
        theme: 'quarto',
        placement: 'bottom-start',
      };
      if (contentFn) {
        config.content = contentFn;
      }
      if (onTriggerFn) {
        config.onTrigger = onTriggerFn;
      }
      if (onUntriggerFn) {
        config.onUntrigger = onUntriggerFn;
      }
      window.tippy(el, config); 
    }
    const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
    for (var i=0; i<noterefs.length; i++) {
      const ref = noterefs[i];
      tippyHover(ref, function() {
        // use id or data attribute instead here
        let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
        try { href = new URL(href).hash; } catch {}
        const id = href.replace(/^#\/?/, "");
        const note = window.document.getElementById(id);
        if (note) {
          return note.innerHTML;
        } else {
          return "";
        }
      });
    }
    const xrefs = window.document.querySelectorAll('a.quarto-xref');
    const processXRef = (id, note) => {
      // Strip column container classes
      const stripColumnClz = (el) => {
        el.classList.remove("page-full", "page-columns");
        if (el.children) {
          for (const child of el.children) {
            stripColumnClz(child);
          }
        }
      }
      stripColumnClz(note)
      if (id === null || id.startsWith('sec-')) {
        // Special case sections, only their first couple elements
        const container = document.createElement("div");
        if (note.children && note.children.length > 2) {
          container.appendChild(note.children[0].cloneNode(true));
          for (let i = 1; i < note.children.length; i++) {
            const child = note.children[i];
            if (child.tagName === "P" && child.innerText === "") {
              continue;
            } else {
              container.appendChild(child.cloneNode(true));
              break;
            }
          }
          if (window.Quarto?.typesetMath) {
            window.Quarto.typesetMath(container);
          }
          return container.innerHTML
        } else {
          if (window.Quarto?.typesetMath) {
            window.Quarto.typesetMath(note);
          }
          return note.innerHTML;
        }
      } else {
        // Remove any anchor links if they are present
        const anchorLink = note.querySelector('a.anchorjs-link');
        if (anchorLink) {
          anchorLink.remove();
        }
        if (window.Quarto?.typesetMath) {
          window.Quarto.typesetMath(note);
        }
        if (note.classList.contains("callout")) {
          return note.outerHTML;
        } else {
          return note.innerHTML;
        }
      }
    }
    for (var i=0; i<xrefs.length; i++) {
      const xref = xrefs[i];
      tippyHover(xref, undefined, function(instance) {
        instance.disable();
        let url = xref.getAttribute('href');
        let hash = undefined; 
        if (url.startsWith('#')) {
          hash = url;
        } else {
          try { hash = new URL(url).hash; } catch {}
        }
        if (hash) {
          const id = hash.replace(/^#\/?/, "");
          const note = window.document.getElementById(id);
          if (note !== null) {
            try {
              const html = processXRef(id, note.cloneNode(true));
              instance.setContent(html);
            } finally {
              instance.enable();
              instance.show();
            }
          } else {
            // See if we can fetch this
            fetch(url.split('#')[0])
            .then(res => res.text())
            .then(html => {
              const parser = new DOMParser();
              const htmlDoc = parser.parseFromString(html, "text/html");
              const note = htmlDoc.getElementById(id);
              if (note !== null) {
                const html = processXRef(id, note);
                instance.setContent(html);
              } 
            }).finally(() => {
              instance.enable();
              instance.show();
            });
          }
        } else {
          // See if we can fetch a full url (with no hash to target)
          // This is a special case and we should probably do some content thinning / targeting
          fetch(url)
          .then(res => res.text())
          .then(html => {
            const parser = new DOMParser();
            const htmlDoc = parser.parseFromString(html, "text/html");
            const note = htmlDoc.querySelector('main.content');
            if (note !== null) {
              // This should only happen for chapter cross references
              // (since there is no id in the URL)
              // remove the first header
              if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
                note.children[0].remove();
              }
              const html = processXRef(null, note);
              instance.setContent(html);
            } 
          }).finally(() => {
            instance.enable();
            instance.show();
          });
        }
      }, function(instance) {
      });
    }
        let selectedAnnoteEl;
        const selectorForAnnotation = ( cell, annotation) => {
          let cellAttr = 'data-code-cell="' + cell + '"';
          let lineAttr = 'data-code-annotation="' +  annotation + '"';
          const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
          return selector;
        }
        const selectCodeLines = (annoteEl) => {
          const doc = window.document;
          const targetCell = annoteEl.getAttribute("data-target-cell");
          const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
          const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
          const lines = annoteSpan.getAttribute("data-code-lines").split(",");
          const lineIds = lines.map((line) => {
            return targetCell + "-" + line;
          })
          let top = null;
          let height = null;
          let parent = null;
          if (lineIds.length > 0) {
              //compute the position of the single el (top and bottom and make a div)
              const el = window.document.getElementById(lineIds[0]);
              top = el.offsetTop;
              height = el.offsetHeight;
              parent = el.parentElement.parentElement;
            if (lineIds.length > 1) {
              const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
              const bottom = lastEl.offsetTop + lastEl.offsetHeight;
              height = bottom - top;
            }
            if (top !== null && height !== null && parent !== null) {
              // cook up a div (if necessary) and position it 
              let div = window.document.getElementById("code-annotation-line-highlight");
              if (div === null) {
                div = window.document.createElement("div");
                div.setAttribute("id", "code-annotation-line-highlight");
                div.style.position = 'absolute';
                parent.appendChild(div);
              }
              div.style.top = top - 2 + "px";
              div.style.height = height + 4 + "px";
              div.style.left = 0;
              let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
              if (gutterDiv === null) {
                gutterDiv = window.document.createElement("div");
                gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
                gutterDiv.style.position = 'absolute';
                const codeCell = window.document.getElementById(targetCell);
                const gutter = codeCell.querySelector('.code-annotation-gutter');
                gutter.appendChild(gutterDiv);
              }
              gutterDiv.style.top = top - 2 + "px";
              gutterDiv.style.height = height + 4 + "px";
            }
            selectedAnnoteEl = annoteEl;
          }
        };
        const unselectCodeLines = () => {
          const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
          elementsIds.forEach((elId) => {
            const div = window.document.getElementById(elId);
            if (div) {
              div.remove();
            }
          });
          selectedAnnoteEl = undefined;
        };
          // Handle positioning of the toggle
      window.addEventListener(
        "resize",
        throttle(() => {
          elRect = undefined;
          if (selectedAnnoteEl) {
            selectCodeLines(selectedAnnoteEl);
          }
        }, 10)
      );
      function throttle(fn, ms) {
      let throttle = false;
      let timer;
        return (...args) => {
          if(!throttle) { // first call gets through
              fn.apply(this, args);
              throttle = true;
          } else { // all the others get throttled
              if(timer) clearTimeout(timer); // cancel #2
              timer = setTimeout(() => {
                fn.apply(this, args);
                timer = throttle = false;
              }, ms);
          }
        };
      }
        // Attach click handler to the DT
        const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
        for (const annoteDlNode of annoteDls) {
          annoteDlNode.addEventListener('click', (event) => {
            const clickedEl = event.target;
            if (clickedEl !== selectedAnnoteEl) {
              unselectCodeLines();
              const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
              if (activeEl) {
                activeEl.classList.remove('code-annotation-active');
              }
              selectCodeLines(clickedEl);
              clickedEl.classList.add('code-annotation-active');
            } else {
              // Unselect the line
              unselectCodeLines();
              clickedEl.classList.remove('code-annotation-active');
            }
          });
        }
    const findCites = (el) => {
      const parentEl = el.parentElement;
      if (parentEl) {
        const cites = parentEl.dataset.cites;
        if (cites) {
          return {
            el,
            cites: cites.split(' ')
          };
        } else {
          return findCites(el.parentElement)
        }
      } else {
        return undefined;
      }
    };
    var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
    for (var i=0; i<bibliorefs.length; i++) {
      const ref = bibliorefs[i];
      const citeInfo = findCites(ref);
      if (citeInfo) {
        tippyHover(citeInfo.el, function() {
          var popup = window.document.createElement('div');
          citeInfo.cites.forEach(function(cite) {
            var citeDiv = window.document.createElement('div');
            citeDiv.classList.add('hanging-indent');
            citeDiv.classList.add('csl-entry');
            var biblioDiv = window.document.getElementById('ref-' + cite);
            if (biblioDiv) {
              citeDiv.innerHTML = biblioDiv.innerHTML;
            }
            popup.appendChild(citeDiv);
          });
          return popup.innerHTML;
        });
      }
    }
  });
  </script>
<nav class="page-navigation">
  <div class="nav-page nav-page-previous">
  </div>
  <div class="nav-page nav-page-next">
      <a href="./books/en-US/book.html" class="pagination-link" aria-label="The Book">
        <span class="nav-page-text"><span class="chapter-title">The Book</span></span> <i class="bi bi-arrow-right-short"></i>
      </a>
  </div>
</nav>
</div> <!-- /content -->




</body></html>